package com.cryptoneedle.garden.service.crud.garden.source;


import cn.hutool.v7.core.tree.MapTree;
import cn.hutool.v7.core.tree.TreeNode;
import cn.hutool.v7.core.tree.TreeNodeConfig;
import cn.hutool.v7.core.tree.TreeUtil;
import com.cryptoneedle.garden.dto.SourceTreeDto;
import com.cryptoneedle.garden.entity.source.constraint.*;
import com.cryptoneedle.garden.entity.source.enhance.SourceDimension;
import com.cryptoneedle.garden.entity.source.metadata.*;
import com.cryptoneedle.garden.enums.SourceTreeNodeType;
import com.cryptoneedle.garden.key.ColumnKey;
import com.cryptoneedle.garden.key.DatabaseKey;
import com.cryptoneedle.garden.key.TableKey;
import com.cryptoneedle.garden.key.source.constraint.ConstraintCheckKey;
import com.cryptoneedle.garden.key.source.constraint.ConstraintColumnKey;
import com.cryptoneedle.garden.key.source.constraint.IndexKey;
import com.cryptoneedle.garden.key.source.enhance.DimensionKey;
import com.cryptoneedle.garden.key.source.metadata.ServerKey;
import com.cryptoneedle.garden.repository.source.constraint.*;
import com.cryptoneedle.garden.repository.source.enhance.SourceDimensionRepository;
import com.cryptoneedle.garden.repository.source.metadata.*;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * <p>description:  </p>
 *
 * @author CryptoNeedle
 * @date 2025-10-23
 */
@Slf4j
@Service
@Transactional
public class FoundSourceService {

    @Autowired
    private FoundSourceService select;
    @Autowired
    private AddSourceService add;
    @Autowired
    private PatchSourceService patch;
    @Autowired
    private SaveSourceService save;
    @Autowired
    private DeleteSourceService delete;

    @Resource
    private SourceServerRepository sourceServerRepository;
    @Resource
    private SourceCatalogRepository sourceCatalogRepository;
    @Resource
    private SourceDatabaseRepository sourceDatabaseRepository;
    @Resource
    private SourceTableRepository sourceTableRepository;
    @Resource
    private SourceColumnRepository sourceColumnRepository;

    @Resource
    private SourceConstraintCheckRepository sourceConstraintCheckRepository;
    @Resource
    private SourceConstraintForeignRepository sourceConstraintForeignRepository;
    @Resource
    private SourceConstraintPrimaryRepository sourceConstraintPrimaryRepository;
    @Resource
    private SourceConstraintUniqueRepository sourceConstraintUniqueRepository;
    @Resource
    private SourceIndexUniqueRepository sourceIndexUniqueRepository;

    @Resource
    private SourceDimensionRepository sourceDimensionRepository;

    /**
     * Tree
     */

    public List<MapTree<Object>> tree(Boolean displayServer, Boolean displayTable) {
        List<SourceTreeDto> sourceTreeDtos = new ArrayList<>();

        // 服务器
        if (displayServer) {
            sourceTreeDtos.addAll(select
                    .servers()
                    .stream()
                    .map(server -> new SourceTreeDto()
                            .setId(server.inetAddress())
                            .setParentId(null)
                            .setName(server.inetAddress())
                            .setNodeType(SourceTreeNodeType.SERVER.name())
                            .setDatabaseType(null)
                            .setEnabled(true)
                            .setActiveDimensionName(null))
                    .toList());
        }

        // SourceCatalog
        sourceTreeDtos.addAll(select
                .catalogs()
                .stream()
                .map(catalog -> new SourceTreeDto()
                        .setId(catalog.getCatalogName())
                        .setParentId(displayServer ? catalog.inetAddress() : null)
                        .setName(catalog.getCatalogName())
                        .setNodeType(SourceTreeNodeType.CATALOG.name())
                        .setDatabaseType(catalog.getDatabaseType())
                        .setEnabled(catalog.getEnabled())
                        .setActiveDimensionName(null))
                .toList());

        // 数据库
        sourceTreeDtos.addAll(select
                .databasesEnabled()
                .stream()
                .map(database -> new SourceTreeDto()
                        .setId(database.getDatabaseName())
                        .setParentId(database.getCatalogName())
                        .setName(database.getDatabaseName())
                        .setNodeType(SourceTreeNodeType.DATABASE.name())
                        .setDatabaseType(null)
                        .setEnabled(database.getEnabled())
                        .setActiveDimensionName(null))
                .toList());

        // 表
        if (displayTable) {
            sourceTreeDtos.addAll(select
                    .tablesEnabled()
                    .stream()
                    .map(table -> new SourceTreeDto()
                            .setId(table.getTableName())
                            .setParentId(table.getDatabaseName())
                            .setName(table.getTableName())
                            .setNodeType(SourceTreeNodeType.TABLE.name())
                            .setDatabaseType(null)
                            .setEnabled(table.getEnabled())
                            .setActiveDimensionName(table.getActiveDimensionName()))
                    .toList());
        }

        AtomicInteger sort = new AtomicInteger();
        List<TreeNode<String>> treeNodeList = sourceTreeDtos
                .stream()
                .map(treeDto -> new TreeNode<String>()
                        .setId(treeDto.getId())
                        .setParentId(treeDto.getParentId())
                        .setName(treeDto.getName())
                        .setWeight(sort.getAndIncrement())
                        .setExtra(new HashMap<>() {{
                            put("nodeType", treeDto.getNodeType());
                            put("databaseType", treeDto.getDatabaseType());
                        }}))
                .toList();

        TreeNodeConfig treeNodeConfig = new TreeNodeConfig();
        treeNodeConfig.setNameKey("name");
        treeNodeConfig.setDeep(4);

        return TreeUtil.build(treeNodeList, null, treeNodeConfig, (object, treeNode) -> {
            treeNode.setParentId(object.getParentId());
            treeNode.setId(object.getId());
            treeNode.setName(object.getName());
            treeNode.setWeight(object.getWeight());
            treeNode.putExtra("nodeType", object.getExtra().get("nodeType"));
            treeNode.putExtra("databaseType", object.getExtra().get("databaseType"));
            treeNode.putExtra("enabled", object.getExtra().get("enabled"));
            treeNode.putExtra("activeDimensionName", object.getExtra().get("activeDimensionName"));
        });
    }

    /**
     * SourceServer
     */

    public SourceServer server(ServerKey key) {
        return sourceServerRepository.findById(key).orElse(null);
    }

    public SourceServer server(String host, Integer port) {
        return select.server(new ServerKey(host, port));
    }

    public SourceServer server(String catalogName) {
        SourceCatalog catalog = select.catalog(catalogName);
        return select.server(catalog);
    }

    public SourceServer server(SourceCatalog catalog) {
        return select.server(catalog.serverKey());
    }

    public List<SourceServer> servers() {
        return sourceServerRepository.servers();
    }

    /**
     * SourceCatalog
     */

    public SourceCatalog catalog(String key) {
        return sourceCatalogRepository.findById(key).orElse(null);
    }

    public SourceCatalog catalogByDorisCatalogName(String dorisCatalogName) {
        return sourceCatalogRepository.catalogByDorisCatalogName(dorisCatalogName);
    }

    public List<SourceCatalog> catalogs() {
        return sourceCatalogRepository.catalogs();
    }

    public List<SourceCatalog> catalogsEnabled() {
        return sourceCatalogRepository.catalogsEnabled();
    }

    public List<SourceCatalog> catalogs(SourceServer server) {
        return sourceCatalogRepository.catalogsByServer(server.getHost(), server.getPort());
    }

    /**
     * AbstractDatabase
     */

    public SourceDatabase database(DatabaseKey key) {
        return sourceDatabaseRepository.findById(key).orElse(null);
    }

    public SourceDatabase database(String catalogName, String databaseName) {
        return select.database(new DatabaseKey(catalogName, databaseName));
    }

    public List<SourceDatabase> databases() {
        return sourceDatabaseRepository.databases();
    }

    public List<SourceDatabase> databases(SourceCatalog catalog) {
        return select.databases(catalog.getCatalogName());
    }

    public List<SourceDatabase> databases(String catalogName) {
        return sourceDatabaseRepository.databases(catalogName);
    }

    public List<SourceDatabase> databasesEnabled() {
        return sourceDatabaseRepository.databasesEnabled();
    }

    public List<SourceDatabase> databasesEnabled(SourceCatalog catalog) {
        return select.databasesEnabled(catalog.getCatalogName());
    }

    public List<SourceDatabase> databasesEnabled(String catalogName) {
        return sourceDatabaseRepository.databasesEnabled(catalogName);
    }

    /**
     * SourceTable
     */

    public SourceTable table(TableKey key) {
        return sourceTableRepository.findById(key).orElse(null);
    }

    public SourceTable table(String catalogName, String databaseName, String tableName) {
        return select.table(new TableKey(catalogName, databaseName, tableName));
    }

    public List<SourceTable> tables() {
        return sourceTableRepository.tables();
    }

    public List<SourceTable> tables(SourceCatalog catalog) {
        return select.tables(catalog.getCatalogName());
    }

    public List<SourceTable> tables(String catalogName) {
        return sourceTableRepository.tables(catalogName);
    }

    public List<SourceTable> tables(SourceDatabase database) {
        return select.tables(database.getCatalogName(), database.getDatabaseName());
    }

    public List<SourceTable> tables(String catalogName, String databaseName) {
        return sourceTableRepository.tables(catalogName, databaseName);
    }

    public List<SourceTable> tablesEnabled() {
        return sourceTableRepository.tablesEnabled();
    }

    public List<SourceTable> tablesEnabled(SourceCatalog catalog) {
        return select.tablesEnabled(catalog.getCatalogName());
    }

    public List<SourceTable> tablesEnabled(String catalogName) {
        return sourceTableRepository.tablesEnabled(catalogName);
    }

    public List<SourceTable> tablesEnabled(SourceDatabase database) {
        return select.tablesEnabled(database.getCatalogName(), database.getDatabaseName());
    }

    public List<SourceTable> tablesEnabled(String catalogName, String databaseName) {
        return sourceTableRepository.tablesEnabled(catalogName, databaseName);
    }

    /**
     * SourceColumn
     */

    public SourceColumn column(ColumnKey key) {
        return sourceColumnRepository.findById(key).orElse(null);
    }

    public SourceColumn column(String catalogName, String databaseName, String tableName, String columnName) {
        return select.column(new ColumnKey(catalogName, databaseName, tableName, columnName));
    }

    public List<SourceColumn> columns(SourceCatalog catalog) {
        return select.columns(catalog.getCatalogName());
    }

    public List<SourceColumn> columns(String catalogName) {
        return sourceColumnRepository.columns(catalogName);
    }

    public List<SourceColumn> columns(SourceDatabase database) {
        return select.columns(database.getCatalogName(), database.getDatabaseName());
    }

    public List<SourceColumn> columns(String catalogName, String databaseName) {
        return sourceColumnRepository.columns(catalogName, databaseName);
    }

    public List<SourceColumn> columns(SourceTable table) {
        return select.columns(table.getCatalogName(), table.getDatabaseName(), table.getTableName());
    }

    public List<SourceColumn> columns(String catalogName, String databaseName, String tableName) {
        return sourceColumnRepository.columns(catalogName, databaseName, tableName);
    }

    public List<SourceColumn> columnsWithDimension(SourceTable table) {
        return sourceColumnRepository.columnsWithDimension(table.getCatalogName(), table.getDatabaseName(), table.getTableName());
    }

    public List<SourceColumn> columnsWithoutDimension(SourceTable table) {
        return sourceColumnRepository.columnsWithoutDimension(table.getCatalogName(), table.getDatabaseName(), table.getTableName());
    }

    /**
     * SourceConstraintCheck
     */

    public SourceConstraintCheck constraintCheck(ConstraintCheckKey key) {
        return sourceConstraintCheckRepository.findById(key).orElse(null);
    }

    public SourceConstraintCheck constraintCheck(String catalogName, String databaseName, String tableName, String constraintName) {
        return select.constraintCheck(new ConstraintCheckKey(catalogName, databaseName, tableName, constraintName));
    }

    public List<SourceConstraintCheck> constraintChecks(SourceCatalog catalog) {
        return select.constraintChecks(catalog.getCatalogName());
    }

    public List<SourceConstraintCheck> constraintChecks(String catalogName) {
        return sourceConstraintCheckRepository.constraintChecks(catalogName);
    }

    public List<SourceConstraintCheck> constraintChecks(SourceDatabase database) {
        return select.constraintChecks(database.getCatalogName(), database.getDatabaseName());
    }

    public List<SourceConstraintCheck> constraintChecks(String catalogName, String databaseName) {
        return sourceConstraintCheckRepository.constraintChecks(catalogName, databaseName);
    }

    public List<SourceConstraintCheck> constraintChecks(SourceTable table) {
        return select.constraintChecks(table.getCatalogName(), table.getDatabaseName(), table.getTableName());
    }

    public List<SourceConstraintCheck> constraintChecks(String catalogName, String databaseName, String tableName) {
        return sourceConstraintCheckRepository.constraintChecks(catalogName, databaseName, tableName);
    }

    /**
     * SourceConstraintForeign
     */

    public SourceConstraintForeign constraintForeign(ConstraintColumnKey key) {
        return sourceConstraintForeignRepository.findById(key).orElse(null);
    }

    public SourceConstraintForeign constraintForeign(String catalogName, String databaseName, String tableName, String constraintName, String columnName) {
        return select.constraintForeign(new ConstraintColumnKey(catalogName, databaseName, tableName, constraintName, columnName));
    }

    public List<SourceConstraintForeign> constraintForeigns(SourceCatalog catalog) {
        return select.constraintForeigns(catalog.getCatalogName());
    }

    public List<SourceConstraintForeign> constraintForeigns(String catalogName) {
        return sourceConstraintForeignRepository.constraintForeigns(catalogName);
    }

    public List<SourceConstraintForeign> constraintForeigns(SourceDatabase database) {
        return select.constraintForeigns(database.getCatalogName(), database.getDatabaseName());
    }

    public List<SourceConstraintForeign> constraintForeigns(String catalogName, String databaseName) {
        return sourceConstraintForeignRepository.constraintForeigns(catalogName, databaseName);
    }

    public List<SourceConstraintForeign> constraintForeigns(SourceTable table) {
        return select.constraintForeigns(table.getCatalogName(), table.getDatabaseName(), table.getTableName());
    }

    public List<SourceConstraintForeign> constraintForeigns(String catalogName, String databaseName, String tableName) {
        return sourceConstraintForeignRepository.constraintForeigns(catalogName, databaseName, tableName);
    }

    public List<SourceConstraintForeign> constraintForeigns(SourceColumn column) {
        return select.constraintForeigns(column.getCatalogName(), column.getDatabaseName(), column.getTableName(), column.getColumnName());
    }

    public List<SourceConstraintForeign> constraintForeigns(String catalogName, String databaseName, String tableName, String columnName) {
        return sourceConstraintForeignRepository.constraintForeigns(catalogName, databaseName, tableName, columnName);
    }

    /**
     * SourceConstraintPrimary
     */

    public SourceConstraintPrimary constraintPrimary(ConstraintColumnKey key) {
        return sourceConstraintPrimaryRepository.findById(key).orElse(null);
    }

    public SourceConstraintPrimary constraintPrimary(String catalogName, String databaseName, String tableName, String constraintName, String columnName) {
        return select.constraintPrimary(new ConstraintColumnKey(catalogName, databaseName, tableName, constraintName, columnName));
    }

    public List<SourceConstraintPrimary> constraintPrimaries(SourceCatalog catalog) {
        return select.constraintPrimaries(catalog.getCatalogName());
    }

    public List<SourceConstraintPrimary> constraintPrimaries(String catalogName) {
        return sourceConstraintPrimaryRepository.constraintPrimaries(catalogName);
    }

    public List<SourceConstraintPrimary> constraintPrimaries(SourceDatabase database) {
        return select.constraintPrimaries(database.getCatalogName(), database.getDatabaseName());
    }

    public List<SourceConstraintPrimary> constraintPrimaries(String catalogName, String databaseName) {
        return sourceConstraintPrimaryRepository.constraintPrimaries(catalogName, databaseName);
    }

    public List<SourceConstraintPrimary> constraintPrimaries(SourceTable table) {
        return select.constraintPrimaries(table.getCatalogName(), table.getDatabaseName(), table.getTableName());
    }

    public List<SourceConstraintPrimary> constraintPrimaries(String catalogName, String databaseName, String tableName) {
        return sourceConstraintPrimaryRepository.constraintPrimaries(catalogName, databaseName, tableName);
    }

    public List<SourceConstraintPrimary> constraintPrimaries(SourceColumn column) {
        return select.constraintPrimaries(column.getCatalogName(), column.getDatabaseName(), column.getTableName(), column.getColumnName());
    }

    public List<SourceConstraintPrimary> constraintPrimaries(String catalogName, String databaseName, String tableName, String columnName) {
        return sourceConstraintPrimaryRepository.constraintPrimaries(catalogName, databaseName, tableName, columnName);
    }

    /**
     * SourceConstraintUnique
     */

    public SourceConstraintUnique constraintUnique(ConstraintColumnKey key) {
        return sourceConstraintUniqueRepository.findById(key).orElse(null);
    }

    public SourceConstraintUnique constraintUnique(String catalogName, String databaseName, String tableName, String constraintName, String columnName) {
        return select.constraintUnique(new ConstraintColumnKey(catalogName, databaseName, tableName, constraintName, columnName));
    }

    public List<SourceConstraintUnique> constraintUniques(SourceCatalog catalog) {
        return select.constraintUniques(catalog.getCatalogName());
    }

    public List<SourceConstraintUnique> constraintUniques(String catalogName) {
        return sourceConstraintUniqueRepository.constraintUniques(catalogName);
    }

    public List<SourceConstraintUnique> constraintUniques(SourceDatabase database) {
        return select.constraintUniques(database.getCatalogName(), database.getDatabaseName());
    }

    public List<SourceConstraintUnique> constraintUniques(String catalogName, String databaseName) {
        return sourceConstraintUniqueRepository.constraintUniques(catalogName, databaseName);
    }

    public List<SourceConstraintUnique> constraintUniques(SourceTable table) {
        return select.constraintUniques(table.getCatalogName(), table.getDatabaseName(), table.getTableName());
    }

    public List<SourceConstraintUnique> constraintUniques(String catalogName, String databaseName, String tableName) {
        return sourceConstraintUniqueRepository.constraintUniques(catalogName, databaseName, tableName);
    }

    public List<SourceConstraintUnique> constraintUniques(SourceColumn column) {
        return select.constraintUniques(column.getCatalogName(), column.getDatabaseName(), column.getTableName(), column.getColumnName());
    }

    public List<SourceConstraintUnique> constraintUniques(String catalogName, String databaseName, String tableName, String columnName) {
        return sourceConstraintUniqueRepository.constraintUniques(catalogName, databaseName, tableName, columnName);
    }

    /**
     * SourceIndexUnique
     */

    public SourceIndexUnique indexUnique(IndexKey key) {
        return sourceIndexUniqueRepository.findById(key).orElse(null);
    }

    public SourceIndexUnique indexUnique(String catalogName, String databaseName, String tableName, String indexName, String columnName) {
        return select.indexUnique(new IndexKey(catalogName, databaseName, tableName, indexName, columnName));
    }

    public List<SourceIndexUnique> indexUniques(SourceCatalog catalog) {
        return select.indexUniques(catalog.getCatalogName());
    }

    public List<SourceIndexUnique> indexUniques(String catalogName) {
        return sourceIndexUniqueRepository.indexUniques(catalogName);
    }

    public List<SourceIndexUnique> indexUniques(SourceDatabase database) {
        return select.indexUniques(database.getCatalogName(), database.getDatabaseName());
    }

    public List<SourceIndexUnique> indexUniques(String catalogName, String databaseName) {
        return sourceIndexUniqueRepository.indexUniques(catalogName, databaseName);
    }

    public List<SourceIndexUnique> indexUniques(SourceTable table) {
        return select.indexUniques(table.getCatalogName(), table.getDatabaseName(), table.getTableName());
    }

    public List<SourceIndexUnique> indexUniques(String catalogName, String databaseName, String tableName) {
        return sourceIndexUniqueRepository.indexUniques(catalogName, databaseName, tableName);
    }

    public List<SourceIndexUnique> indexUniques(SourceColumn column) {
        return select.indexUniques(column.getCatalogName(), column.getDatabaseName(), column.getTableName(), column.getColumnName());
    }

    public List<SourceIndexUnique> indexUniques(String catalogName, String databaseName, String tableName, String columnName) {
        return sourceIndexUniqueRepository.indexUniques(catalogName, databaseName, tableName, columnName);
    }

    /**
     * SourceDimension
     */

    public SourceDimension dimension(DimensionKey key) {
        return sourceDimensionRepository.findById(key).orElse(null);
    }

    public SourceDimension dimension(String catalogName, String databaseName, String tableName, String dimensionName, String columnName) {
        return select.dimension(new DimensionKey(catalogName, databaseName, tableName, dimensionName, columnName));
    }

    public List<SourceDimension> dimensions(SourceCatalog catalog) {
        return select.dimensions(catalog.getCatalogName());
    }

    public List<SourceDimension> dimensions(String catalogName) {
        return sourceDimensionRepository.dimensions(catalogName);
    }

    public List<SourceDimension> dimensions(SourceDatabase database) {
        return select.dimensions(database.getCatalogName(), database.getDatabaseName());
    }

    public List<SourceDimension> dimensions(String catalogName, String databaseName) {
        return sourceDimensionRepository.dimensions(catalogName, databaseName);
    }

    public List<SourceDimension> dimensions(SourceTable table) {
        return select.dimensions(table.getCatalogName(), table.getDatabaseName(), table.getTableName());
    }

    public List<SourceDimension> dimensions(String catalogName, String databaseName, String tableName) {
        return sourceDimensionRepository.dimensions(catalogName, databaseName, tableName);
    }

    public List<SourceDimension> dimensions(SourceColumn column) {
        return select.dimensions(column.getCatalogName(), column.getDatabaseName(), column.getTableName(), column.getColumnName());
    }

    public List<SourceDimension> dimensions(String catalogName, String databaseName, String tableName, String columnName) {
        return sourceDimensionRepository.dimensions(catalogName, databaseName, tableName, columnName);
    }

    public List<SourceDimension> dimensionsEnabled(SourceTable table) {
        return sourceDimensionRepository.dimensionsEnabled(table.getCatalogName(), table.getDatabaseName(), table.getTableName());
    }

    public List<SourceDimension> dimensionsEnabled(String catalogName, String databaseName, String tableName) {
        return sourceDimensionRepository.dimensionsEnabled(catalogName, databaseName, tableName);
    }
}